/**
 * This file is part of 1genia trampoline
 * Copyright (C) 2007-2008 1genia (contact@1genia.com)
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; version 3 of the License. 
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details. 
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; see the file COPYING.TXT.  If not,
 * write to the Free Software Foundation, Inc., 51 Franklin Street,
 * Fifth Floor, Boston, MA 02110-1301, USA. 
 **/
package com.genia.toolbox.projects.toolbox_basics_project.business.dao.impl;

import java.util.List;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import com.genia.toolbox.basics.exception.technical.TechnicalException;
import com.genia.toolbox.persistence.bean.Persistable;
import com.genia.toolbox.persistence.criteria.Criteria;
import com.genia.toolbox.persistence.dao.AbstractDAO;
import com.genia.toolbox.persistence.exception.ObjectIsNotPersistableException;
import com.genia.toolbox.persistence.exception.PersistenceException;
import com.genia.toolbox.persistence.jcr.manager.DataContainerManager;
import com.genia.toolbox.projects.toolbox_basics_project.bean.model.I18nDynamicString;
import com.genia.toolbox.projects.toolbox_basics_project.bean.model.I18nString;
import com.genia.toolbox.projects.toolbox_basics_project.bean.model.Language;
import com.genia.toolbox.projects.toolbox_basics_project.bean.model.Template;
import com.genia.toolbox.projects.toolbox_basics_project.bean.model.TemplateType;
import com.genia.toolbox.projects.toolbox_basics_project.bean.model.impl.I18nDynamicStringImpl;
import com.genia.toolbox.projects.toolbox_basics_project.bean.model.impl.I18nStringImpl;
import com.genia.toolbox.projects.toolbox_basics_project.bean.model.impl.TemplateImpl;
import com.genia.toolbox.projects.toolbox_basics_project.bean.model.impl.TemplateTypeImpl;
import com.genia.toolbox.projects.toolbox_basics_project.business.dao.ByNameReferenceableDAO;
import com.genia.toolbox.projects.toolbox_basics_project.business.dao.I18nSearchHelperDao;
import com.genia.toolbox.projects.toolbox_basics_project.business.dao.TemplateDAO;
import com.genia.toolbox.projects.toolbox_basics_project.spring.manager.LanguageManager;

/**
 * implementation of {@link TemplateDAO}.
 */
public class TemplateDAOImpl
    extends AbstractDAO
    implements TemplateDAO
{

  /**
   * the {@link DataContainerManager} to use.
   */
  private DataContainerManager dataContainerManager;

  /**
   * the {@link LanguageManager} to use.
   */
  private LanguageManager languageManager;

  /**
   * the {@link ByNameReferenceableDAO} to use.
   */
  private ByNameReferenceableDAO byNameReferenceableDAO;

  /**
   * the {@link I18nSearchHelperDao}.
   */
  private I18nSearchHelperDao i18nSearchHelperDao;



  /**
   * getter for the dataContainerManager property.
   * 
   * @return the dataContainerManager
   */
  public DataContainerManager getDataContainerManager()
  {
    return dataContainerManager;
  }



  /**
   * getter for the languageManager property.
   * 
   * @return the languageManager
   */
  public LanguageManager getLanguageManager()
  {
    return languageManager;
  }



  /**
   * returns the {@link Template} associated to the given reference name.
   * 
   * @param templateType
   *          the type of the {@link Template} to retrieve
   * @param referenceName
   *          the reference name of the {@link Template}, if this value is
   *          <code>null</code>, the root {@link Template} will be returned
   * @return the {@link Template} associated to the given reference name, if no
   *         such {@link Template} exists prior to the call of this function, on
   *         such {@link Template} will be created
   * @throws TechnicalException
   *           if an error occured
   * @see com.genia.toolbox.projects.toolbox_basics_project.business.dao.TemplateDAO#getOrCreateTemplate(com.genia.toolbox.projects.toolbox_basics_project.bean.model.TemplateType,
   *      java.lang.String)
   */
  @Transactional(rollbackFor = TechnicalException.class)
  public Template getOrCreateTemplate(final TemplateType templateType, final String referenceName)
      throws TechnicalException
  {
    String actualReferenceName = referenceName;
    if (actualReferenceName == null) {
      actualReferenceName = TemplateDAO.ROOT_TEMPLATE_REFERENCE_NAME;
    }
    Template template = getTemplate(templateType, actualReferenceName);

    if (template == null) {
      template = new TemplateImpl();
      template.setReferenceName(actualReferenceName);
      template.setTemplateType(templateType);
      if (!TemplateDAO.ROOT_TEMPLATE_REFERENCE_NAME.equals(actualReferenceName)) {
        template.setParentTemplate(getOrCreateTemplate(templateType, null));
      }
      template.setUuid(getDataContainerManager().newDirectory());

      I18nDynamicString name = new I18nDynamicStringImpl();
      Language language = getLanguageManager().getDefaultLanguage();
      if (language != null) {
        I18nString i18nString = new I18nStringImpl();
        i18nString.setLanguage(language);
        i18nString.setValue(actualReferenceName);
        name.addI18nValue(i18nString);
      }

      template.setName(name);

      save((TemplateImpl) template);
    }
    return template;
  }



  /**
   * returns the {@link TemplateType} associated to the given reference name.
   * 
   * @param referenceName
   *          the reference name of the {@link TemplateType}
   * @return the {@link TemplateType} associated to the given reference name, if
   *         no such {@link TemplateType} exists prior to the call of this
   *         function, on such {@link TemplateType} will be created
   * @throws TechnicalException
   *           if an error occured
   * @see com.genia.toolbox.projects.toolbox_basics_project.business.dao.TemplateDAO#getOrCreateTemplateType(java.lang.String)
   */
  @Transactional(rollbackFor = TechnicalException.class)
  public TemplateType getOrCreateTemplateType(String referenceName)
      throws TechnicalException
  {
    TemplateType templateType = getTemplateType(referenceName);
    if (templateType == null) {
      templateType = new TemplateTypeImpl();
      templateType.setReferenceName(referenceName);
      save((TemplateTypeImpl) templateType);
    }
    return templateType;
  }



  /**
   * returns the {@link Template} associated to the given reference name.
   * 
   * @param templateType
   *          the type of the {@link Template} to retrieve
   * @param referenceName
   *          the reference name of the {@link Template}, if this value is
   *          <code>null</code>, the root {@link Template} will be returned
   * @return the {@link Template} associated to the given reference name or
   *         <code>null</code> if no such {@link Template} exists prior to the
   *         call of this function
   * @throws TechnicalException
   *           if an error occured
   * @see com.genia.toolbox.projects.toolbox_basics_project.business.dao.TemplateDAO#getTemplate(com.genia.toolbox.projects.toolbox_basics_project.bean.model.TemplateType,
   *      java.lang.String)
   */
  public Template getTemplate(TemplateType templateType, String referenceName)
      throws TechnicalException
  {
    String actualReferenceName = referenceName;
    if (actualReferenceName == null) {
      actualReferenceName = TemplateDAO.ROOT_TEMPLATE_REFERENCE_NAME;
    }
    Criteria<Template> criteria = getCriteria(Template.class);
    criteria.addRestriction(equals(getPropertyField(Template.NAME_TEMPLATE_TYPE), getConstantField(templateType)));
    criteria.addRestriction(equals(getPropertyField(Template.NAME_REFERENCE_NAME), getConstantField(actualReferenceName)));
    return simpleFindUnique(criteria);
  }



  /**
   * returns the {@link List} of {@link Template} having the given
   * {@link TemplateType}.
   * 
   * @param templateType
   *          the {@link TemplateType} of the templates to return
   * @return the {@link List} of {@link Template} having the given
   *         {@link TemplateType}
   * @throws TechnicalException
   *           if an error occured
   * @see com.genia.toolbox.projects.toolbox_basics_project.business.dao.TemplateDAO#getTemplates(TemplateType)
   */
  public List<Template> getTemplates(final TemplateType templateType)
      throws TechnicalException
  {

    Criteria<Template> criteria = getCriteria(Template.class);
    criteria.addRestriction(equals(getPropertyField(Template.NAME_TEMPLATE_TYPE), getConstantField(templateType)));

    return simpleFind(criteria);

  }



  /**
   * return a {@link List} of {@link Template} having the given
   * {@link TemplateType} and the name.
   * 
   * @param templateType
   *          the {@link TemplateType} of the templates to return
   * @param name
   *          the name of the {@link Template}, if this value is
   *          <code>null</code>, it returns all {@link Template} having given
   *          the {@link TemplateType}
   * @return a {@link List} of {@link Template} having the given
   *         {@link TemplateType} and the name
   * @throws PersistenceException
   *           if an error occured
   * @see com.genia.toolbox.projects.toolbox_basics_project.business.dao.TemplateDAO#findTemplates(com.genia.toolbox.projects.toolbox_basics_project.bean.model.TemplateType,
   *      java.lang.String)
   */
  public List<Template> findTemplates(TemplateType templateType, String name)
      throws PersistenceException
  {

    Criteria<Template> criteria = getCriteria(Template.class);

    if (templateType != null) {
      criteria.addRestriction(equals(getPropertyField(Template.NAME_TEMPLATE_TYPE), getConstantField(templateType)));
    }

    if (StringUtils.hasText(name)) {
      getI18nSearchHelperDao().addI18nDynamicStringRestriction(criteria, criteria.getMainType(), Template.NAME_NAME, name);
    }

    criteria.addOrder(asc(getPropertyField(Template.NAME_NAME)));

    return simpleFind(criteria);
  }



  /**
   * return all {@link TemplateType}.
   * 
   * @return all {@link TemplateType}.
   * @throws PersistenceException
   *           if an error occured
   */
  public List<TemplateType> getAllTemplateTypes()
      throws PersistenceException
  {
    return super.getAll(TemplateType.class);
  }



  /**
   * get the {@link Template} by its identifier.
   * 
   * @param identifier
   *          the {@link Template} identifier
   * @return the {@link Template}
   * @throws PersistenceException
   *           when error occured.
   */
  public Template getTemplate(Long identifier)
      throws PersistenceException
  {
    return get(TemplateImpl.class, identifier);
  }



  /**
   * get the {@link TemplateType} by its identifier.
   * 
   * @param identifier
   *          the {@link TemplateType} identifier
   * @return the {@link TemplateType}
   * @throws PersistenceException
   *           when error occured.
   */
  public TemplateType getTemplateType(Long identifier)
      throws PersistenceException
  {
    return get(TemplateTypeImpl.class, identifier);
  }



  /**
   * returns the {@link TemplateType} associated to the given reference name.
   * 
   * @param referenceName
   *          the reference name of the {@link TemplateType}
   * @return the {@link TemplateType} associated to the given reference name or
   *         <code>null</code> if no such {@link TemplateType} exists prior to
   *         the call of this function
   * @throws TechnicalException
   *           if an error occured
   * @see com.genia.toolbox.projects.toolbox_basics_project.business.dao.TemplateDAO#getTemplateType(java.lang.String)
   */
  public TemplateType getTemplateType(String referenceName)
      throws TechnicalException
  {
    return getByNameReferenceableDAO().getObjectByReferenceName(TemplateType.class, referenceName);
  }



  /**
   * setter for the dataContainerManager property.
   * 
   * @param dataContainerManager
   *          the dataContainerManager to set
   */
  public void setDataContainerManager(DataContainerManager dataContainerManager)
  {
    this.dataContainerManager = dataContainerManager;
  }



  /**
   * setter for the languageManager property.
   * 
   * @param languageManager
   *          the languageManager to set
   */
  public void setLanguageManager(LanguageManager languageManager)
  {
    this.languageManager = languageManager;
  }



  /**
   * update a {@link Template}.
   * 
   * @param template
   *          the {@link Template} to save
   * @throws TechnicalException
   *           if an error occured
   * @see com.genia.toolbox.projects.toolbox_basics_project.business.dao.TemplateDAO#updateTemplate(com.genia.toolbox.projects.toolbox_basics_project.bean.model.Template)
   */
  public void updateTemplate(Template template)
      throws TechnicalException
  {
    if (!(template instanceof Persistable)) {
      throw new ObjectIsNotPersistableException();
    }
    update((Persistable<?>) template);
  }



  /**
   * delete a {@link Template}.
   * 
   * @param template
   *          the {@link Template} to delete.
   * @throws PersistenceException
   *           when error occured.
   * @see com.genia.toolbox.projects.toolbox_basics_project.business.dao.TemplateDAO#deleteTemplates()
   */
  public void deleteTemplate(Template template)
      throws PersistenceException
  {
    if (template instanceof Persistable) {
      super.delete((Persistable<?>) template);
    }
    else {
      throw new ObjectIsNotPersistableException();
    }
  }



  /**
   * getter for the byNameReferenceableDAO property.
   * 
   * @return the byNameReferenceableDAO
   */
  public ByNameReferenceableDAO getByNameReferenceableDAO()
  {
    return byNameReferenceableDAO;
  }



  /**
   * setter for the byNameReferenceableDAO property.
   * 
   * @param byNameReferenceableDAO
   *          the byNameReferenceableDAO to set
   */
  public void setByNameReferenceableDAO(ByNameReferenceableDAO byNameReferenceableDAO)
  {
    this.byNameReferenceableDAO = byNameReferenceableDAO;
  }



  /**
   * getter for the i18nSearchHelperDao property.
   * 
   * @return the i18nSearchHelperDao
   */
  public I18nSearchHelperDao getI18nSearchHelperDao()
  {
    return i18nSearchHelperDao;
  }



  /**
   * setter for the i18nSearchHelperDao property.
   * 
   * @param searchHelperDao
   *          the i18nSearchHelperDao to set
   */
  public void setI18nSearchHelperDao(I18nSearchHelperDao searchHelperDao)
  {
    i18nSearchHelperDao = searchHelperDao;
  }

}
